﻿##################################################################################################
## lanWOLf.ps1 - Version 1.2 - Ben Shaw
## https://lanwolf.codeplex.com/
###################################################################################################
cls
$currentPath=Split-Path ((Get-Variable MyInvocation -Scope 0).Value).MyCommand.Path

#Config file to load
$ConfigFile = "$currentPath\lanWOLf.config.xml"

#Hashtable to store config
[hashtable]$Cfg = @{}

Function Main(){

	$Cfg=ConfigCheck
	
	if ( $($Cfg.ErrNum) -eq 0){
		
		$CompCsv = Import-Csv $Cfg.Complist
		$SubnetAry = $CompCsv | select -Skip 1 | select "IPSUBNET" -uniq | Sort-Object "IPSUBNET"
		
		# Show Menu and get users choice
		[string]$Choice = ShowMenu($SubnetAry)
		
		# Number of choices
		$SubCount = $SubnetAry.Count
		
		# If the choice is "A" wake all
		if ($Choice.ToUpper() -eq "A"){
			Write-Host "You choose ALL"
			foreach ($Snet in $SubnetAry){
				if ($($Snet.IPSUBNET) -ne ""){WakeSubnet($Snet.IPSUBNET)}
			}
		}
		# If > 0 but within bounds wake that particular subnet
		elseif ( ([int]$Choice -ge 0) -and ([int]$Choice -lt $SubCount) ){
			Write-Host "You choose $Choice"
			WakeSubnet($SubnetAry[$Choice].IPSUBNET)
		}
		else {
			"Not a choice bub!"
		}
		Write-Host "### Done ###"
	}
	else {Write-Host $($Cfg.ErrMsg)}
	
	
}

#######################################
# Present the menu to user.
#######################################
Function ShowMenu ($SubnetAry){
	$Menu = "`tA)`tAll Subnets`n"
	for ($i=0; $i -lt $SubnetAry.Count; $i++){
		$CompName = $SubnetAry[$i].IPSUBNET
		$Menu = $Menu + "`t${i})`t$CompName`n"
	}
	$Menu = $Menu + "`n"
	$Choice = Read-Host  "$Menu Which subnet you would like to wake?"
	return $Choice
}

#######################################
# Sends WOL to a subnet.
#######################################
Function WakeSubnet($Subnet){
	Write-Host "Waking $Subnet"
	Write-Host "`n`tLooking for a Master"
	[int]$Status = $null
	$WakeAry = $CompCsv | Where-Object {$_.IPSUBNET -eq $Subnet}
	
	# Find Master for a particular Subnet
	foreach ($Line in $WakeAry){
		$MasterComp = ""
		CheckComp($($Line.NAME))([ref]$Status)
		if ($Debug){"DEBUG STATUS $($Status)"}
		if ($Status -eq 0){
			$MasterComp = $Line.NAME
			break
		}
	}
	
	# Send WOL to all machines on that subnet
	if ($MasterComp -ne ""){
		"`t>> Master: $MasterComp"
		Write-Host "`n`tSend WOL Signal"
		foreach ($Line in $WakeAry){
			$Args = "-s -d -n5 \\$MasterComp wolcmd.exe $($Line.MACADDR) $($Line.IPADDR) $($Line.IPSUBNET) 7"
			Write-Host "`t>> Sending WOL to $($Line.NAME) $($Line.IPADDR) $($Line.MACADDR)"
			
			if ($Debug){"$($Cfg.Psexec) $Args"}
			$pinfo = New-Object System.Diagnostics.ProcessStartInfo
			$pinfo.FileName = $Cfg.Psexec
			$pinfo.RedirectStandardError = $true
			$pinfo.RedirectStandardOutput = $true
			$pinfo.UseShellExecute = $false
			$pinfo.Arguments = $Args
			$p = New-Object System.Diagnostics.Process
			$p.StartInfo = $pinfo
			$p.Start() | Out-Null
			
			if ($Cfg.ProcessOut){
				$p.WaitForExit()
				$output = $p.StandardOutput.ReadToEnd()
				$output += $p.StandardError.ReadToEnd()
				if ($Debug){ write-host "DEBUG Exit Code: $($p.ExitCode)" }
				if ($StdOut){ $output }
			}
			else{
				$p.StartInfo = $pinfo
				$p.Start() | Out-Null
			}
		}
	}
	else {
		Write-Host "`t>> Master not found."
	}
}

#######################################
# Checks if computer is ready to send WOL commands
# $Computer = the computer to check
# $Status = The status of the remote comp. 0=Good to be master.
#######################################
Function CheckComp($Computer, [ref]$Status){
	$Status.value = 0
	$CompWol = "\\${Computer}\$($Cfg.WolPath)"
	Write-Output "`t>> Pinging $Computer"
	$pingresult = Get-WmiObject win32_pingstatus -f "address='$Computer'"
    if ($pingresult.statuscode -ne 0) {$Status.value++}
	else {
		if ($Debug){"DEBUG: Copy-Item $CompWol $CompWol\WolCmd.exe"}
		Write-Output "`t>> Checking $Computer wolcmd.exe"
		
		if (test-path -path "$CompWol\WolCmd.exe"){
			write-host "`t>> WolCmd.exe available on $Computer"
		}
		else {
			write-host "`t>> WolCmd.exe not there. Attempt to copy"
			Copy-Item "$($Cfg.WolCmd)" "$CompWol" -ErrorAction SilentlyContinue
			
			# If copy is success then increment the stuatus by 2
			if (!(test-path -path "$CompWol\WolCmd.exe")) {
				$Status.value = $Status.value + 2
				write-host "`t>> Unable to copy WolCmd.exe"
			}
		}
	}
	if ($Debug){"DEBUG CheckComp Status -> $($Status.value)"}
}

#######################################
# Checks config file for errors, returns hash of config information.
######################################
Function ConfigCheck(){
	[xml]$Config = get-content $ConfigFile
	[hashtable]$Cfg = @{}
	$Cfg.Psexec = $Config.Config.Psexec
	$Cfg.WolPath = $Config.Config.WolPath
	$Cfg.WolCmd = $Config.Config.WolCmd
	$Cfg.Complist = $Config.Config.Complist
	$Cfg.ProcessOut = [System.Convert]::ToBoolean($Config.Config.ProcessOut)
	$Cfg.Debug = [System.Convert]::ToBoolean($Config.Config.Debug)
	
	$Cfg.ErrNum = 0
	$Cfg.ErrMsg = ""
	if (!(test-path $Cfg.Complist)){
		$Cfg.ErrNum++; 
		$Cfg.ErrMsg = "$($Cfg.ErrMsg) Can not find Complist, check config. `n"
	}
	else {
		$Cfg.LastWrite = (Get-Item $Cfg.Complist | select-object LastWriteTime).LastWriteTime 
		$Cfg.Age = ((Get-Date) - ($Cfg.LastWrite)).days
		write-host "Complist file last written $($Cfg.LastWrite) ($($Cfg.Age) days old)."
	}
	if (!(test-path $Cfg.Psexec)){$Cfg.ErrNum++; $Cfg.ErrMsg = "$($Cfg.ErrMsg) Error: Can not find Psexec, check config. `n";}
	if (!(test-path $Cfg.WolCmd)){$Cfg.ErrNum++;; $Cfg.ErrMsg = "$($Cfg.ErrMsg) Error: Can not find WolCmd, check config. `n";}
	if (!($Cfg.ProcessOut -is [Boolean])){$Cfg.ErrNum++; $Cfg.ErrMsg = "$($Cfg.ErrMsg) <ProcessOut> is not boolean, check config. `n"}
	if (!($Cfg.Debug -is [Boolean])){$Cfg.ErrNum++; $Cfg.ErrMsg = "$($Cfg.ErrMsg)<Debug> is not boolean, check config. `n"}
	
	if ($Debug){write-host "$ErrNum $ErrMsg"}
	return $Cfg
}

main

